package org.callisto.handlers;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import javax.swing.border.EmptyBorder;

import org.callisto.EventType;
import org.callisto.StatisticType;
import org.callisto.gui.Gauge;
import org.callisto.model.Event;
import org.callisto.model.Keyboard;

public class GUIOutputHandler extends KeyboardObserver {

	static StringBuffer buffer = new StringBuffer();
	Dashboard dash = null;
	
	public GUIOutputHandler(Keyboard keyboard) {
		super(keyboard);
		System.out.println("[Graphical output handler]");
		dash = new Dashboard("MIDI Dashboard");
	}
	
	public void close() {
		dash.dispose();
	}
	
	@Override
	public void handleEvent(Event event) {
		dash.handleEvent(event);
	}
	
	@SuppressWarnings("serial")
	class Dashboard extends JFrame {
		private int stroke_index=0;
		Plotter pressurePlot = null;
		Plotter durationPlot = null;
		Gauge pressureGauge = null;
		Gauge durationGauge = null;
		JLabel console = null;
		
		public Dashboard(String title) throws HeadlessException {
			super(title);
			setLayout(new BorderLayout());
			((JComponent) getContentPane()).setBorder(new EmptyBorder(10, 10, 10, 10));
			setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		    add(new JLabel("Performance Dashboard 2.0",SwingConstants.CENTER),BorderLayout.NORTH);
		    add(new JLabel("Console"),BorderLayout.SOUTH);
		   
		    // *** Control panel
		    Box controlPanel = Box.createVerticalBox();
		    controlPanel.add(new JCheckBox("Auto range") {{this.setFont(new Font("Arial",Font.PLAIN,10));}});
		    controlPanel.add(Box.createVerticalStrut(10));
		    controlPanel.add(new JSeparator());
		    
		    controlPanel.add(new JLabel("Summary"));
		    final ButtonGroup group = new ButtonGroup();
		    controlPanel.add(new JRadioButton("Min") {{this.setFont(new Font("Arial",Font.PLAIN,10)); group.add(this); }});
		    controlPanel.add(new JRadioButton("Med") {{this.setFont(new Font("Arial",Font.PLAIN,10)); group.add(this); }});
		    controlPanel.add(new JRadioButton("Avg") {{this.setFont(new Font("Arial",Font.PLAIN,10)); group.add(this); }});
		    controlPanel.add(new JRadioButton("Std") {{this.setFont(new Font("Arial",Font.PLAIN,10)); group.add(this); }});
		    controlPanel.add(new JRadioButton("Max") {{this.setFont(new Font("Arial",Font.PLAIN,10)); group.add(this); }});
		    controlPanel.add(Box.createVerticalStrut(10));
		    controlPanel.add(new JSeparator());
		    
		    controlPanel.add(new JLabel("Target"));
		    controlPanel.add(new JLabel("Pressure") {{this.setFont(new Font("Arial",Font.PLAIN,10));}});
		    controlPanel.add(new JTextField() {{this.setMaximumSize(new Dimension(300,30));}});
		    controlPanel.add(new JLabel("Duration") {{this.setFont(new Font("Arial",Font.PLAIN,10));}});
		    controlPanel.add(new JTextField() {{this.setMaximumSize(new Dimension(300,30));}});
		    controlPanel.add(Box.createVerticalStrut(10));
		    controlPanel.add(new JSeparator());
		    controlPanel.add(new JButton("Reset"),JButton.CENTER_ALIGNMENT);
		    add(controlPanel,BorderLayout.WEST);
		    
		    // *** Plot panel
		    JPanel plotsPanel = new JPanel();
		    plotsPanel.setLayout(new BoxLayout(plotsPanel,BoxLayout.Y_AXIS));
		    add(plotsPanel,BorderLayout.CENTER);
		    pressurePlot = new Plotter(EventType.NOTE_ON);
		    plotsPanel.add(pressurePlot);
		    durationPlot = new Plotter(EventType.NOTE_OFF);
		    plotsPanel.add(durationPlot);
		    
		    // *** Gauge panel
		    JPanel gaugePanel = new JPanel();
		    gaugePanel.setLayout(new BoxLayout(gaugePanel,BoxLayout.Y_AXIS));
		    add(gaugePanel,BorderLayout.EAST);
		    pressureGauge = new Gauge(keyboard,EventType.NOTE_ON,200);
		    durationGauge = new Gauge(keyboard,EventType.NOTE_OFF,200);
		    gaugePanel.add(Box.createRigidArea(new Dimension(200,1)));
		    gaugePanel.add(pressureGauge);
		    gaugePanel.add(durationGauge);
		    
		    setVisible(true);
		    setSize(500, 300);
		    pack();
		}		
		
		public void handleEvent (Event event) {
			repaint();
			if (event.getStatus() == EventType.NOTE_ON) {
				stroke_index++;
				pressurePlot.repaint();
				pressureGauge.repaint();
			}
			if (event.getStatus() == EventType.NOTE_OFF) {
				stroke_index++;
				durationPlot.repaint();
				durationGauge.repaint();
			}
		}
		
		class Plotter extends JPanel {
			
			JLabel hiY, midY, loY;

			public void setHi(float value) {
				hiY.setText(String.format("%.0f",value));
			}
			
			public void setMid(float value) {
				midY.setText(String.format("%.0f",value));
			}
			
			public void setLo(float value) {
				loY.setText(String.format("%.0f",value));
			}
			
			public Plotter(EventType eventType) {
				super();
				setLayout(new BorderLayout());
				((JComponent) getContentPane()).setBorder(new BevelBorder(10));
				add(new PlotterCanvas(eventType),BorderLayout.CENTER);
				add(new JLabel("Graph: " + eventType, JLabel.CENTER),BorderLayout.NORTH);
				{
					JPanel yAxis = new JPanel();
					yAxis.setLayout(new BoxLayout(yAxis,BoxLayout.Y_AXIS));
					hiY = new JLabel("100%"); hiY.setFont(new Font("Arial",Font.PLAIN,10));
					yAxis.add(hiY);
					yAxis.add(Box.createVerticalGlue());
					midY = new JLabel("50%"); midY.setFont(new Font("Arial",Font.PLAIN,10));
					yAxis.add(midY);
					yAxis.add(Box.createVerticalGlue());
					loY = new JLabel("0%"); loY.setFont(new Font("Arial",Font.PLAIN,10));
					yAxis.add(loY);
					add(yAxis,BorderLayout.WEST);
				}
				add(new JLabel("X",JLabel.CENTER),BorderLayout.SOUTH);
				add(new JLabel("Legend"), BorderLayout.EAST);
			}
		}
		
		class PlotterCanvas extends JPanel {
			
			EventType eventType;
			int windowPixWitdh;
			int windowPixHeight;
			
			public PlotterCanvas(EventType eventType) {
				super();
				this.eventType=eventType;
				setLayout(new FlowLayout());
				setPreferredSize(new Dimension(400,200));
				setBackground(new Color(0x40,0x40,0x40));
			}
			
			private void drawStatLine(Graphics2D g2, float measure, float maxMeasure, Color color) {
				int lineheight = 0;
				lineheight = (int) (windowPixHeight * measure / maxMeasure);
				int summaryPixY = windowPixHeight - lineheight;
				g2.setColor(color);
				g2.drawLine(0,summaryPixY,windowPixWitdh,summaryPixY);
				//Globals.logln(String.format("(measure,max,angle,tipX,tipY)=(%.2f,%.2f,%.2f,%.2f,%.2f)\n",measure,maxMeasure,angle,tipX,tipY));
			}
			
			@Override
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				Graphics2D g2 = (Graphics2D) g;
				if (stroke_index>0) {
					// get pixel dimensions for drawing area
					windowPixWitdh = getWidth();
					windowPixHeight = getHeight();
					g.setColor(Color.BLACK);
					g.drawRect(0, 0, windowPixWitdh, windowPixHeight);
					final int eventPixWidth = 10;
					int windowEventWidth = windowPixWitdh / eventPixWidth;
					// get index boundaries for displayed events
					int eventCount = keyboard.getEventCount(eventType);
					int windowEventMin = (eventCount < windowEventWidth) ? 0 : eventCount - windowEventWidth;
					int windowEventMax = eventCount;
					float maxMeasure = keyboard.getMeasureStatistic(eventType, StatisticType.MAX);
					for (int i=windowEventMin; i<windowEventMax; i++) {
						Event event = keyboard.getEvent(eventType,i);
						int renderEventIndex = i - windowEventMin;
						int currentMeasure=event.getMeasure();
						maxMeasure = (currentMeasure > maxMeasure) ? currentMeasure : maxMeasure;
						((Plotter) getParent()).setHi((float) maxMeasure);
						((Plotter) getParent()).setMid(maxMeasure/2f);
						((Plotter) getParent()).setLo(0f);
						int eventPixX = renderEventIndex * eventPixWidth;
						int barheight=0;
						barheight=(int) (windowPixHeight  * currentMeasure / maxMeasure);
						final int MAX_NOTE=108;
						final int MIN_NOTE=21;
						g2.setColor(new Color(0xaaaaff));
						g2.fillRect(eventPixX,windowPixHeight - barheight,eventPixWidth,barheight);
						g2.setColor(new Color(0x00c000));
						int notePixY= (int)((float)(event.getNote() - MIN_NOTE) / (MAX_NOTE - MIN_NOTE) * windowPixHeight );
						g2.fillOval(
								eventPixX + eventPixWidth / 2, 
								windowPixHeight - notePixY, 
								8, 4);
//						Globals.logln(
//									"note=" + event.getNote() + 
//									",type=" + event.getStatus() + ", i=" + i + ", renderIndex=" + renderEventIndex +
//									", currentMeasure=" + currentMeasure + ", maxMeasure=" + maxMeasure +
//									", leftx=" + eventPixX + ", eventPixWidth=" + eventPixWidth + ", barheight=" + barheight +
//									", pheight=" + notePixY
//									);
					}
					// *** Draw summary line
					
					Event lastEvent = keyboard.getLastEvent(eventType);
					if (lastEvent != null) {
						float currentMeasure = lastEvent.getMeasure();
						float minMeasure = keyboard.getMeasureStatistic(eventType, StatisticType.MIN);
						float meanMeasure = keyboard.getMeasureStatistic(eventType, StatisticType.MEAN);
						float medianMeasure = keyboard.getMeasureStatistic(eventType, StatisticType.MEDIAN);
						drawStatLine(g2, currentMeasure, maxMeasure, Color.WHITE);
						drawStatLine(g2, minMeasure, maxMeasure, Color.RED);
						drawStatLine(g2, maxMeasure, maxMeasure, Color.RED);
						drawStatLine(g2, medianMeasure, maxMeasure, Color.YELLOW);
						drawStatLine(g2, meanMeasure, maxMeasure, Color.ORANGE);
					}
				}
			}
		}
	}
}
